<!--
  The contents of this file are subject to the terms of the Common Development and
  Distribution License (the License). You may not use this file except in compliance with the
  License.

  You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
  specific language governing permission and limitations under the License.

  When distributing Covered Software, include this CDDL Header Notice in each file and include
  the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
  Header, with the fields enclosed by brackets [] replaced by your own identifying
  information: "Portions Copyright [year] [name of copyright owner]".

  Copyright 2008 Sun Microsystems, Inc.
  ! -->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common">
  <!--
    This XSLT file contains utility templates which can be used for any
    generating Java code.
  -->
  <xsl:import href="text-utilities.xsl" />
  <xsl:output method="text" encoding="us-ascii" />
  <!--
    Add a copyright notice to the top of a Java source file.
    
    TODO: it would be nice to generate the copyright year automatically.
  -->
  <xsl:template name="copyright-notice">
    <xsl:value-of
      select="concat('/*&#xa;',
                     ' * The contents of this file are subject to the terms of the Common Development and&#xa;',
                     ' * Distribution License (the License). You may not use this file except in compliance with the&#xa;',
                     ' * License.&#xa;',
                     ' *&#xa;',
                     ' * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the&#xa;',
                     ' * specific language governing permission and limitations under the License.&#xa;',
                     ' *&#xa;',
                     ' * When distributing Covered Software, include this CDDL Header Notice in each file and include&#xa;',
                     ' * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL&#xa;',
                     ' * Header, with the fields enclosed by brackets [] replaced by your own identifying&#xa;',
                     ' * information: &quot;Portions Copyright [year] [name of copyright owner]&quot;.&#xa;',
                     ' *&#xa;',
                     ' * Copyright 2008 Sun Microsystems, Inc.&#xa;',
                     ' */&#xa;')" />
  </xsl:template>
  <!--
    Convert an entity or property ID to a Java mixed-cased name.
    For example, the string "my-string-value" will be converted to
    the string "myStringValue".
    
    @param value
    The ID string to be converted to a Java name.
  -->
  <xsl:template name="name-to-java">
    <xsl:param name="value" />
    <xsl:if test="string-length($value)">
      <xsl:choose>
        <xsl:when test="contains($value, '-')">
          <xsl:variable name="head"
            select="substring-before($value, '-')" />
          <xsl:variable name="tail"
            select="substring-after($value, '-')" />
          <xsl:call-template name="to-title-case">
            <xsl:with-param name="value" select="$head" />
          </xsl:call-template>
          <xsl:call-template name="name-to-java">
            <xsl:with-param name="value" select="$tail" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="to-title-case">
            <xsl:with-param name="value" select="$value" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>
  <!--
    Convert an entity or property ID to a Java constant name.
    For example, the string "my-string-value" will be converted to
    the string "MY_STRING_VALUE".
    
    @param value
    The ID string to be converted to a Java constant.
  -->
  <xsl:template name="name-to-java-constant">
    <xsl:param name="value" />
    <xsl:value-of
      select="translate($value,
                        'abcdefghijklmnopqrstuvwxyz-',
                        'ABCDEFGHIJKLMNOPQRSTUVWXYZ_')" />
  </xsl:template>
  <!--
    Add a Java comment. This template handles two levels of
    indentation: the indentation string for the first line, and a
    second indentation string used for subsequent lines. The template
    will output the content wrapping at the nearest word boundary to
    column 70.
    
    @param indent-text
    The indentation text used for the first line.
    
    @param indent-text2
    The indentation text used for all lines except
    the first - defaults to the value of indent-text.
    
    @param content
    The content to be output in the comment.
  -->
  <xsl:template name="add-java-comment">
    <xsl:param name="indent-text" />
    <xsl:param name="indent-text2" select="$indent-text" />
    <xsl:param name="content" />
    <xsl:call-template name="format-text">
      <xsl:with-param name="indent-text" select="$indent-text" />
      <xsl:with-param name="indent-text2" select="$indent-text2" />
      <xsl:with-param name="wrap-column" select="'70'" />
      <xsl:with-param name="content" select="$content" />
    </xsl:call-template>
  </xsl:template>
  <!--
    Add a Java comment at the specified indentation.
    
    This template handles embedded newline characters
    and will also indent individual lines according to
    the number of leading spaces they contain.
    
    @param indent
    The indentation column for the comment.
    
    @param content
    The content to be output in the comment.
  -->
  <xsl:template name="add-java-comment2">
    <xsl:param name="indent" select="/.." />
    <xsl:param name="content" select="/.." />
    <!--  Compute the indentation string. -->
    <xsl:variable name="indent-text">
      <xsl:call-template name="add-indent">
        <xsl:with-param name="indent" select="$indent + 1" />
      </xsl:call-template>
      <xsl:value-of select="'*'" />
    </xsl:variable>
    <!--  Output the comment header. -->
    <xsl:call-template name="add-indent">
      <xsl:with-param name="indent" select="$indent" />
    </xsl:call-template>
    <xsl:value-of select="'/**&#xa;'" />
    <!--  Output the comment contents. -->
    <xsl:call-template name="add-java-comment-line">
      <xsl:with-param name="indent-text" select="$indent-text" />
      <xsl:with-param name="content" select="$content" />
    </xsl:call-template>
    <!--  Output the header trailer. -->
    <xsl:value-of select="concat($indent-text, '/&#xa;')" />
  </xsl:template>
  <!-- Creates a padding string of the required length. -->
  <xsl:template name="add-indent">
    <xsl:param name="indent" select="/.." />
    <xsl:if test="$indent > 0">
      <xsl:value-of select="' '" />
      <xsl:call-template name="add-indent">
        <xsl:with-param name="indent" select="$indent - 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
  <xsl:template name="add-java-comment-line">
    <xsl:param name="indent-text" select="/.." />
    <xsl:param name="content" select="/.." />
    <!--  Get the next line. -->
    <xsl:variable name="head"
      select="substring-before($content, '&#xa;')" />
    <xsl:variable name="tail"
      select="substring-after($content, '&#xa;')" />
    <!--
      Case #1 - content is empty
      Case #2 - no newline
      Case #3 - contains a new line
      Case #3.1 - begins with newline
      Case #3.2 - ends with newline
    -->
    <xsl:choose>
      <xsl:when test="string-length($content) = 0">
        <!-- Do nothing. -->
      </xsl:when>
      <xsl:when test="not(contains($content, '&#xa;'))">
        <!-- Single line of text. -->
        <xsl:call-template name="java-format-line">
          <xsl:with-param name="indent-text" select="$indent-text" />
          <xsl:with-param name="line" select="$content" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <!-- Output the first line and repeat for remaining lines. -->
        <xsl:call-template name="java-format-line">
          <xsl:with-param name="indent-text" select="$indent-text" />
          <xsl:with-param name="line" select="$head" />
        </xsl:call-template>
        <xsl:call-template name="add-java-comment-line">
          <xsl:with-param name="indent-text" select="$indent-text" />
          <xsl:with-param name="content" select="$tail" />
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <!-- Formats a line of comment text. -->
  <xsl:template name="java-format-line">
    <xsl:param name="indent-text" select="/.." />
    <xsl:param name="line" select="/.." />
    <!--  First count the number of leading spaces to determine the indent. -->
    <xsl:variable name="leading-spaces">
      <xsl:call-template name="java-format-line-help">
        <xsl:with-param name="line" select="$line" />
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="content"
      select="substring($line, $leading-spaces + 1)" />
    <xsl:variable name="padding1">
      <xsl:value-of select="$indent-text" />
      <xsl:call-template name="add-indent">
        <xsl:with-param name="indent" select="$leading-spaces" />
      </xsl:call-template>
    </xsl:variable>
    <!-- We need to use indent2 for certain javadoc keywords. -->
    <xsl:variable name="padding2">
      <xsl:choose>
        <xsl:when test="starts-with($content, '@return')">
          <xsl:value-of select="concat($padding1, '        ')" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$padding1" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- Now output the line, wrapping as necessary. -->
    <xsl:call-template name="format-text">
      <xsl:with-param name="indent-text" select="$padding1" />
      <xsl:with-param name="indent-text2" select="$padding2" />
      <xsl:with-param name="wrap-column" select="'70'" />
      <xsl:with-param name="content" select="$content" />
    </xsl:call-template>
  </xsl:template>
  <!--  Determines the number of leading spaces in the provided string. -->
  <xsl:template name="java-format-line-help">
    <xsl:param name="line" select="/.." />
    <xsl:param name="count" select="0" />
    <xsl:choose>
      <xsl:when test="starts-with($line, ' ')">
        <xsl:call-template name="java-format-line-help">
          <xsl:with-param name="line"
            select="substring-after($line, ' ')" />
          <xsl:with-param name="count" select="$count + 1" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$count" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <!--
    Utility template for removing duplicate values from a node-set.
    
    This template is based on the version published on the XSLT site.
    It is not capable of normalizing nodes - so they must be
    pre-normalized before this template is called.
    
    @param nodes A node-set containing the duplicate nodes.
  -->
  <xsl:template name="set-distinct">
    <xsl:param name="nodes" select="/.." />
    <xsl:call-template name="_set-distinct">
      <xsl:with-param name="nodes" select="$nodes" />
    </xsl:call-template>
  </xsl:template>
  <!-- set-distinct helper template -->
  <xsl:template name="_set-distinct">
    <xsl:param name="nodes" select="/.." />
    <xsl:param name="distinct" select="/.." />
    <xsl:choose>
      <xsl:when test="$nodes">
        <xsl:variable name="value" select="$nodes[1]" />
        <xsl:choose>
          <xsl:when test="$distinct[. = $value]">
            <xsl:call-template name="_set-distinct">
              <xsl:with-param name="distinct" select="$distinct" />
              <xsl:with-param name="nodes"
                select="$nodes[position() > 1]" />
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="_set-distinct">
              <xsl:with-param name="distinct"
                select="$distinct | $nodes[1]" />
              <xsl:with-param name="nodes"
                select="$nodes[position() > 1]" />
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="$distinct" mode="set-distinct" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <!-- set-distinct helper template -->
  <xsl:template match="node()|@*" mode="set-distinct">
    <xsl:copy-of select="." />
  </xsl:template>
  <!-- 
    Generate a set of import statements.
    
    This template takes a result tree fragment as a parameter
    containing elements of the form:
    
    <import>java.net.InetAddress</import>
    <import>...</import>
    
    This template will normalize each element and remove duplicates
    before generating the output.
    
    @param imports The result tree fragment containing the import elements.
  -->
  <xsl:template name="generate-import-statements">
    <xsl:param name="imports" select="/.." />
    <!-- 
      Normalize the import elements since the set-distinct
      template cannot handle additional whitespace
    -->
    <xsl:variable name="normalized-imports">
      <xsl:for-each select="exsl:node-set($imports)/import">
        <xsl:element name="import">
          <xsl:value-of select="normalize-space()" />
        </xsl:element>
      </xsl:for-each>
    </xsl:variable>
    <!--
      Now remove the duplicates
    -->
    <xsl:variable name="unique">
      <xsl:call-template name="set-distinct">
        <xsl:with-param name="nodes"
          select="exsl:node-set($normalized-imports)/import" />
      </xsl:call-template>
    </xsl:variable>
    <!--
      Now output the import statements
    -->
    <xsl:for-each select="exsl:node-set($unique)/import">
      <xsl:sort select="normalize-space()" />
      <xsl:value-of
        select="concat('import ', normalize-space(),';&#xa;')" />
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
